package com.iot.core.manager.htcls;



import com.iot.core.data.BasePageQto;
import com.iot.core.data.BaseQto;
import com.iot.core.data.entity.HtCls;
import com.iot.core.data.entity.HtCodeLog;
import com.iot.core.data.mapper.HtClsMapper;
import com.iot.core.data.mapper.HtCodeLogMapper;
import com.iot.core.manager.id.HtIdManager;
import com.iot.core.util.MyJSON;
import com.iot.core.util.StreamUtil;
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
import com.mybatisflex.core.query.QueryWrapper;
import io.swagger.v3.oas.annotations.media.Schema;

import jakarta.validation.constraints.*;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.iot.core.data.entity.table.HtClsTableDef.HT_CLS;

@Slf4j

public class HtClsManager {

    @Autowired
    HtCodeLogMapper htCodeLogMapper;
    @Autowired
    HtClsMapper htClsMapper;



    private static volatile HtClsManager instance = null;

    public static HtClsManager getInstance() {
        return instance;
    }
    //2.构造方法设为 private
    public HtClsManager() {
         instance=this;
    }




    List<HtClsDto> lAll;

    public void start() {
        log.info("start");
        lAll=new ArrayList<>();
        Reflections reflections = new Reflections("com.iot");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(Schema.class);
        for (Class<?> clazz : annotated) {
            dealCls(clazz);
        }

    }
    void dealCls(Class<?> clazz){
        var bexists=lAll.stream().filter(a->a.getFullName().equals(clazz.getName())).findFirst().isPresent();
        if(bexists){
            return ;
        }


        var mnew=new HtClsDto();
        mnew.setClazz(clazz);
        mnew.setEname(clazz.getSimpleName());
        mnew.setFullName(clazz.getName());
        mnew.setName(clazz.getSimpleName());
        mnew.setMemo("");
        mnew.setBaseType(clazz.getSuperclass().getSimpleName()  );
        var apiModel= clazz.getAnnotation(Schema.class);
        if(apiModel!=null){
            mnew.setName(apiModel.name() );
            mnew.setMemo(apiModel.description() );
        }
        dealFields(clazz,mnew);


        lAll.add(mnew);
    }
    void dealFields(Class<?> clazz,HtClsDto mcls)
    {



        var  lcolnew=new ArrayList<HtClsColDto>();
        mcls.setFields(lcolnew);

        Field[] flst=clazz.getDeclaredFields();

        Integer sortno=1;
        for(var field:flst){
             var  mnewcol=new HtClsColDto();

            mnewcol.setEname(field.getName());
            mnewcol.setName(field.getName());
            mnewcol.setLx(field.getType().getName() );

            var apiModel= field.getAnnotation(Schema.class);
            if(apiModel!=null){
                mnewcol.setName(apiModel.name() );
                mnewcol.setMemo(apiModel.description() );
            }
            mnewcol.setField(field);
            field.setAccessible(true);

            var blist=List.class.isAssignableFrom(field.getType() );
            if(blist){
                mnewcol.setList(true);
                mnewcol.setLx(field.getGenericType().getTypeName() );
            }else{
                mnewcol.setList(false);
                mnewcol.setLx(field.getType().getTypeName() );

                {
                    NotBlank prop= field.getAnnotation(NotBlank.class);
                    if(prop!=null){
                        mnewcol.setNullable(true);
                        mnewcol.setRequired(true);
                    }else{
                        mnewcol.setNullable(false);
                        mnewcol.setRequired(false);
                    }
                }

                {
                    Size prop= field.getAnnotation(Size.class);
                    if(prop!=null){
                        mnewcol.setVmax( ""+prop.max());
                        mnewcol.setVmin( ""+prop.min());
                    }
                }

                {
                    Min prop= field.getAnnotation(Min.class);
                    if(prop!=null){
                        mnewcol.setVmin( ""+prop.value());
                    }
                }

                {
                    Max prop= field.getAnnotation(Max.class);
                    if(prop!=null){
                        mnewcol.setVmax( ""+prop.value());
                    }
                }

                {
                    Pattern prop= field.getAnnotation(Pattern.class);
                    if(prop!=null){
                        mnewcol.setRegex( ""+prop.regexp());
                    }
                }

                mnewcol.setStr(mnewcol.getLx().equals("java.lang.String") );
                mnewcol.setDate(mnewcol.getLx().equals("java.util.Date") );
                if(mnewcol.getLx().equals("java.lang.Long")){
                    mnewcol.setNum(true);
                }
            }

            lcolnew.add(mnewcol);
        }
    }
//region 公用方法
    public  HtClsDto findByFullName(String fullName){
        return lAll.stream().filter(a->a.getFullName().equals(fullName))
                .findFirst()
                .get();
    }

    public      void dealParam(Object o)
    {
        try {
            dealParamF(o);
        }
        catch(Exception ex) {
        }
    }
    void dealParamF(Object o) throws  Exception
    {
        if (o == null)
        {
            return;
        }
        if(o instanceof BasePageQto pageQto){
            if(StringUtils.hasText(pageQto.getKey())){
                var svtrim = pageQto.getKey() ;
                svtrim=svtrim.replace("'","").trim();
                pageQto.setKey(svtrim);
            }else{
                pageQto.setKey("");
            }
        }
        var ot = o.getClass();

        var op = lAll.stream().filter(a-> a.getFullName().equals(ot.getName())).findFirst();
        if (!op.isPresent())
        {
            return;
        }
        var mcls=op.get();
        var IsQto = o instanceof BaseQto;
        for (HtClsColDto mfield : mcls.getFields() )
        {
            if (mfield.isStr() )
            {
                var v = mfield.getField().get(o);
                if (mfield.isList())
                {
                    if (v == null)
                    {
                        continue;
                    }
                    var l = (List<String>)v;
                    for (var i = 0; i < l.stream().count(); i++)
                    {
                        if (l.get(i) == null)
                        {
                            l.set(i,"") ;
                            continue;
                        }
                        l.set(i,l.get(i).trim() );
                        if (IsQto)
                        {
                            l.set(i,l.get(i).replace("'", "") );
                        }
                    }
                }
                else
                {

                    if (v == null)
                    {
                        mfield.getField().set(o, "");
                    }
                    else
                    {
                        var sv = v.toString();
                        var svtrim = sv.trim();
                        if (IsQto)
                        {
                            svtrim = svtrim.replace("'", "");
                        }
                        if (!sv.equals(svtrim))
                        {
                            mfield.getField().set(o, svtrim);
                        }
                    }
                }
            }
        }
    }


    public Object getFieldV(Object o, String field)
    {
        try
        {
            return getFieldVF(o,field);
        }
        catch (Exception ex)
        {
            return null;
        }

    }
    Object getFieldVF(Object o, String field) throws  Exception
    {
        if (o == null)
        {
            return null;
        }
        var ot = o.getClass();
        var op = lAll.stream().filter(a-> a.getFullName().equals(ot.getName())).findFirst();
        if (!op.isPresent())
        {
            return null;
        }
        var mcls=op.get();

        var op2 = mcls.getFields().stream().filter(a-> a.getEname().equals(field)).findFirst();
        if (!op2.isPresent())
        {
            return null;
        }
        var mfield=op2.get();
        var v = mfield.getField().get(o);
        return v;
    }

    public void setFieldV(Object o, String field,Object v)
    {
        try
        {
            setFieldVF(o, field,v);
        }
        catch (Exception ex)
        {

        }

    }
    void  setFieldVF(Object o, String field, Object v) throws  Exception
    {
        if (o == null)
        {
            return  ;
        }
        var ot = o.getClass();
        var op = lAll.stream().filter(a-> a.getFullName().equals(ot.getName())).findFirst();
        if (!op.isPresent())
        {
            return  ;
        }
        var mcls=op.get();

        var op2 = mcls.getFields().stream().filter(a-> a.getEname().equals(field)).findFirst();
        if (!op2.isPresent())
        {
            return  ;
        }
        var mfield=op2.get();

        mfield.getField().set(o,v);
    }


    public String getFieldStrV(Object o,String field)
    {
        try
        {
            return getFieldStrVF(o, field);
        }
        catch (Exception ex)
        {
            return "";
        }
    }
    String getFieldStrVF(Object o, String field) throws  Exception
    {
        var v = getFieldV(o, field);
        if (v == null)
        {
            return "";
        }
        return v.toString();
    }
    public  void dealStrNullAndTrim(Object o ,Field f){
        try{
            dealStrNullAndTrimF(o,f);
        }
        catch (Exception ex){

        }
    }
    void dealStrNullAndTrimF(Object o ,Field f) throws  Exception  {
        var v=f.get(o);
        if(v==null){
            f.set(o,"");
            return;
        }
        var sv=v.toString().trim();
        if(!v.toString().equals(sv)){
            f.set(o,sv);
        }
    }
    public boolean hasField(Object o, String field)
    {
        try
        {
            return hasFieldF(o, field);
        }
        catch (Exception ex)
        {
            return false;
        }

    }
    boolean hasFieldF(Object o, String field)
    {
        if (o == null)
        {
            return false;
        }

        var ot = o.getClass();
        var op = lAll.stream().filter(a-> a.getFullName().equals(ot.getName())).findFirst();
        if (!op.isPresent())
        {
            return false;
        }
        var mcls=op.get();

        var op2 = mcls.getFields().stream().filter(a-> a.getEname().equals(field)).findFirst();
        if (!op2.isPresent())
        {
            return false;
        }
        return true;
    }
//endregion


    List<HtCls> ldb=null;
    List<HtClsDto> lprj=null;
    String prjName="";
    //region 刷新数据库
    @Transactional(rollbackFor = Exception.class)
    public void refreshToDb(String prjName){
        this.prjName=prjName;
        lprj=lAll.stream().filter(a->a.getFullName().startsWith(prjName)).collect(Collectors.toList());
        dbSelectByPrjName(prjName);
        for(var mcls :lprj){
            refreshToDbCls(mcls);
        }

        for(var mdb:ldb){
            if(mdb.isDeleted()){
                continue;
            }
            var op=lprj.stream().filter(a->a.getFullName().equals(mdb.getFullName()))
                    .findFirst();
            if(!op.isPresent()){

                htClsMapper.deleteById(mdb,true);

                var mcodelog= createHTCodeLogByDto(mdb);
                mcodelog.setMemo("删除");
                htCodeLogMapper.insert(mcodelog,true);
            }
        }
        ldb=null;
        lprj=null;

    }

    private void dbSelectByPrjName(String prjName) {
        LogicDeleteManager.execWithoutLogicDelete(()->{
            var qw=QueryWrapper.create();
            qw.where(HT_CLS.PRJ_NAME.eq(prjName));
            ldb=htClsMapper.selectListByQuery(qw);
        });

    }

    HtCodeLog createHTCodeLogByDto(HtCls  mdb){
        var mcodelog=new HtCodeLog();
        mcodelog.setId(HtIdManager.getInstance().nextLong());
        mcodelog.setBid(mdb.getId());
        mcodelog.setCrtDt(new Date());
        mcodelog.setFullName(mdb.getFullName());
        mcodelog.setLx(mdb.getBaseType());
        mcodelog.setMemo("");
        mcodelog.setName(mdb.getName());
        mcodelog.setPrjName(mdb.getPrjName());
        return mcodelog;
    }

    void refreshToDbCls(HtClsDto mcls){
        var mdb= StreamUtil.get(ldb,a->a.getFullName().equals(mcls.getFullName()));
        if(ObjectUtils.isEmpty(mdb)){
            mdb=new HtCls();
            mdb.setId(HtIdManager.getInstance().nextLong());
            mdb.setBaseType(mcls.getBaseType() );
            mdb.setFullName(mcls.getFullName());
            mdb.setJsonFields(MyJSON.toJSONString(mcls.getFields()));
            mdb.setName(mcls.getName());
            mdb.setMemo(mcls.getMemo());
            mdb.setPrjName(prjName);
            mdb.setCrtDt(new Date());
            mdb.setEdtDt(new Date());

            htClsMapper.insert(mdb );
            var mcodelog= createHTCodeLogByDto(mdb);
            mcodelog.setMemo("新增");
            htCodeLogMapper.insert(mcodelog,true);
        }else{
            var msg = compareCls(mcls, mdb);
            if (mdb.isDeleted())
            {
                mdb.setDeleted(false);
                msg = "删除恢复\r\n"  +msg;
            }
            if (!msg.equals("")) {
                mdb.setEdtDt(new Date());
                mdb.setJsonFields(MyJSON.toJSONString(mcls.getFields()));
                HtCls finalMdb = mdb;
                LogicDeleteManager.execWithoutLogicDelete(()->{
                    htClsMapper.updateById(finalMdb,true);
                });


                var mcodelog= createHTCodeLogByDto(mdb);
                mcodelog.setMemo(msg );
                htCodeLogMapper.insert(mcodelog,true);
            }
        }
    }

    String compareClsField(HtClsColDto mf,HtClsColDto mdb,String fieldName){
        var v=getFieldStrV(mf,fieldName);
        var dbv=getFieldStrV(mdb,fieldName);
        if(!v.equals(dbv )){
            return fieldName+" "+dbv+"=>"+v;
        }
        return "";
    }
    String compareCls(HtClsDto mcls, HtCls mdb )
    {
        var sb = new StringBuilder();
        if (!mcls.getBaseType().equals(mdb.getBaseType()) )
        {
            sb.append("BaseType:")
                    .append(mdb.getBaseType())
                    .append("=>")
                    .append(mcls.getBaseType() )
                    .append("\r\n");
            mdb.setBaseType(mcls.getBaseType());
        }
        if (!mcls.getName().equals(mdb.getName()) )
        {
            sb.append("Name:")
                    .append(mdb.getName())
                    .append("=>")
                    .append(mcls.getName() )
                    .append("\r\n");
            mdb.setName(mcls.getName());
        }
        if (!mcls.getMemo().equals(mdb.getMemo()) )
        {
            sb.append("Memo:")
                    .append(mdb.getMemo())
                    .append("=>")
                    .append(mcls.getMemo() )
                    .append("\r\n");
            mdb.setMemo(mcls.getMemo());
        }


        var ldb = MyJSON.parseArray(mdb.getJsonFields(),HtClsColDto.class);
        for(var mf : mcls.getFields())
        {
            var op=ldb.stream().filter(a->a.getEname().equals(mf.getEname())).findFirst();
            if(!op.isPresent())
            {
                sb.append("字段:")
                        .append(mf.getEname())
                        .append(" 新增")
                        .append("\r\n");
                continue;
            }
            var mfdb=op.get();
            var sf="";
            sf+=compareClsField(mf,mfdb,"name");
            sf+=compareClsField(mf,mfdb,"memo");
            sf+=compareClsField(mf,mfdb,"lx");
            sf+=compareClsField(mf,mfdb,"list");
            sf+=compareClsField(mf,mfdb,"nullable");
            sf+=compareClsField(mf,mfdb,"str");
            sf+=compareClsField(mf,mfdb,"date");
            sf+=compareClsField(mf,mfdb,"num");
            sf+=compareClsField(mf,mfdb,"required");
            sf+=compareClsField(mf,mfdb,"regex");
            sf+=compareClsField(mf,mfdb,"vmin");
            sf+=compareClsField(mf,mfdb,"vmax");

            if(!sf.equals("")){
                sb.append("字段:")
                        .append(mf.getEname())
                        .append( sf )
                        .append("\r\n");
            }
        }
        return sb.toString();
    }
    //endregion
}
